//
//  UIView+SpinAnimation.m
//  EnTT
//
//  Created by 罗 亮富 on 16/6/10.
//
//

#import "UIView+SpinAnimation.h"
#import <objc/runtime.h>

#define ringAnimageKey @"ringAn"

 NSString *kMMRingStrokeAnimationKey = @"mmmaterialdesignspinner.stroke";
 NSString *kMMRingRotationAnimationKey = @"mmmaterialdesignspinner.rotation";

static char rlKey,rDKey,rwKey,rRKey,spinKey;

@implementation UIView (SpinAnimation)


-(void)setRingLayer:(CAShapeLayer *)ringLayer
{
    objc_setAssociatedObject(self, &rlKey, ringLayer, OBJC_ASSOCIATION_ASSIGN);
}

-(CAShapeLayer *)ringLayer
{
    CAShapeLayer *l = nil;
    @try {
        l = objc_getAssociatedObject(self, &rlKey);
    }
    @catch (NSException *exception) {
        l = nil;
    }
    @finally {
        
    }
    
    return l;
}

-(void)setRingDuration:(float)ringDuration
{
    objc_setAssociatedObject(self, &rDKey, [NSNumber numberWithFloat:ringDuration], OBJC_ASSOCIATION_RETAIN);
}

-(float)ringDuration
{
    NSNumber *v = objc_getAssociatedObject(self, &rDKey);
    if(v)
        return v.floatValue;
    else
        return 1.5;
}

-(void)setRingWidth:(CGFloat)ringWidth
{
    objc_setAssociatedObject(self, &rwKey, [NSNumber numberWithFloat:ringWidth], OBJC_ASSOCIATION_RETAIN);
    self.ringLayer.lineWidth = ringWidth;
    self.spinLayer.lineWidth = ringWidth;
}

-(CGFloat)ringWidth
{
    NSNumber *v = objc_getAssociatedObject(self, &rwKey);
    if(v)
        return v.floatValue;
    else
        return 1.5;
}

-(void)setRingRadium:(CGFloat)ringRadium
{
    if(fabs(ringRadium - self.ringRadium) < 0.2)
        return;
    
    objc_setAssociatedObject(self, &rRKey, [NSNumber numberWithFloat:ringRadium], OBJC_ASSOCIATION_RETAIN);
    if(self.ringLayer)
    {
        if(self.isRinging)
        {
            [self ringAnimate:NO];
            [self ringAnimate:YES];
            
        }
    }
}

-(CGFloat)ringRadium
{
    NSNumber *v = objc_getAssociatedObject(self, &rRKey);
    if(v)
        return v.floatValue;
    else
        return (MAX(self.frame.size.width, self.frame.size.height) - self.ringWidth)/2;
}

-(BOOL)isRinging
{
    CABasicAnimation* rotationAnimation = (CABasicAnimation *)[self.ringLayer animationForKey:ringAnimageKey];
    
    return (rotationAnimation != nil);
}

-(void)updateRingLayer
{
    CGFloat radium = self.ringRadium;
    self.ringLayer.frame = self.bounds;
 //   NSLog(@"ringLayer w %.1f self w %.1f",self.ringLayer.frame.size.width, self.frame.size.width);
   // CGRect frame = CGRectMake(self.frame.size.width/2-radium-self.ringWidth/2, self.frame.size.height/2-radium-self.ringWidth/2, radium*2, radium*2);;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.ringLayer.frame.size.width/2, self.ringLayer.frame.size.height/2)
                                                        radius:radium
                                                    startAngle:0
                                                      endAngle:M_PI*1.8
                                                     clockwise:YES];
    self.ringLayer.path = path.CGPath;
}

-(void)ringAnimate:(BOOL)animte
{
    if(animte)
    {
        if(!self.ringLayer)
        {

            CAShapeLayer *theLayer = [CAShapeLayer layer];

            [self.layer addSublayer:theLayer];
            self.ringLayer = theLayer;
            self.ringLayer.lineWidth = self.ringWidth;
            self.ringLayer.strokeColor = self.tintColor.CGColor;
            self.ringLayer.fillColor = nil;
            self.ringLayer.lineCap = @"round";
            self.ringLayer.frame = self.bounds;

            
        }
        
        CABasicAnimation* rotationAnimation = (CABasicAnimation *)[self.ringLayer animationForKey:ringAnimageKey];
        if(!rotationAnimation)
        {
            [self updateRingLayer];
            
            rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
            rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 ];
            [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
            rotationAnimation.duration = self.ringDuration;
            rotationAnimation.repeatCount = NSIntegerMax;//你可以设置到最大的整数值
            rotationAnimation.cumulative = NO;
            rotationAnimation.removedOnCompletion = NO;
            rotationAnimation.fillMode = kCAFillModeForwards;
            [self.ringLayer addAnimation:rotationAnimation forKey:ringAnimageKey];
        }
        
    }
    else if(self.ringLayer)
    {
        [self.ringLayer removeAnimationForKey:ringAnimageKey];
        [self.ringLayer removeFromSuperlayer];
        self.ringLayer = nil;//这里设为nil是因为动态绑定的为OBJC_ASSOCIATION_ASSIGN，而这种类型在对象被销毁后，它的指针不会自动置为nil，再次访问会造成闪退
    }
}

#pragma mark- spin
-(void)setSpinLayer:(CAShapeLayer *)ringLayer
{
    objc_setAssociatedObject(self, &spinKey, ringLayer, OBJC_ASSOCIATION_ASSIGN);
}

-(CAShapeLayer *)spinLayer
{
    CAShapeLayer *l = nil;
    @try {
        l = objc_getAssociatedObject(self, &spinKey);
    }
    @catch (NSException *exception) {
        l = nil;
    }
    @finally {
        
    }
    
    return l;
}

-(void)spinAnimate:(BOOL)animte
{
    if(animte)
    {
        if (!self.spinLayer)
        {
            CGFloat radium = self.ringRadium;
            CAShapeLayer *theLayer = [CAShapeLayer layer];
            theLayer.frame = CGRectMake(self.frame.size.width/2-radium, self.frame.size.height/2-radium, radium*2, radium*2);;
            CGRect frame = theLayer.frame;
            theLayer.strokeColor = self.tintColor.CGColor;
            theLayer.fillColor = nil;
            theLayer.lineWidth = self.ringWidth;
            CGPoint center = CGPointMake(frame.size.width/2, frame.size.height/2);
            CGFloat radius = frame.size.width/2;
            CGFloat startAngle = (CGFloat)(0);
            CGFloat endAngle = (CGFloat)(2*M_PI);
            UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
            theLayer.path = path.CGPath;
            theLayer.lineCap = @"round";
            theLayer.strokeStart = 0.f;
            theLayer.strokeEnd = M_PI*1.85;
            
            [self.layer addSublayer:theLayer];
            self.spinLayer = theLayer;
        }
        CAMediaTimingFunction *tmfuc = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        CABasicAnimation *animation = (CABasicAnimation *)[self.spinLayer animationForKey:kMMRingRotationAnimationKey];
        if(!animation)
        {
            
            animation = [CABasicAnimation animation];
            animation.keyPath = @"transform.rotation";
            animation.duration = 4.f;
            animation.fromValue = @(0.f);
            animation.toValue = @(2 * M_PI);
            animation.repeatCount = INFINITY;
            [self.spinLayer addAnimation:animation forKey:kMMRingRotationAnimationKey];
        }
        
        CAAnimationGroup *animations = (CAAnimationGroup *)[self.spinLayer animationForKey:kMMRingStrokeAnimationKey];
        if(!animations)
        {
            CABasicAnimation *headAnimation = [CABasicAnimation animation];
            headAnimation.keyPath = @"strokeStart";
            headAnimation.duration = 1.f;
            headAnimation.fromValue = @(0.f);
            headAnimation.toValue = @(0.25f);
            headAnimation.timingFunction = tmfuc;
            
            CABasicAnimation *tailAnimation = [CABasicAnimation animation];
            tailAnimation.keyPath = @"strokeEnd";
            tailAnimation.duration = 1.f;
            tailAnimation.fromValue = @(0.f);
            tailAnimation.toValue = @(1.f);
            tailAnimation.timingFunction = tmfuc;
            
            
            CABasicAnimation *endHeadAnimation = [CABasicAnimation animation];
            endHeadAnimation.keyPath = @"strokeStart";
            endHeadAnimation.beginTime = 1.f;
            endHeadAnimation.duration = 0.5f;
            endHeadAnimation.fromValue = @(0.25f);
            endHeadAnimation.toValue = @(1.f);
            endHeadAnimation.timingFunction = tmfuc;
            
            CABasicAnimation *endTailAnimation = [CABasicAnimation animation];
            endTailAnimation.keyPath = @"strokeEnd";
            endTailAnimation.beginTime = 1.f;
            endTailAnimation.duration = 0.5f;
            endTailAnimation.fromValue = @(1.f);
            endTailAnimation.toValue = @(1.f);
            endTailAnimation.timingFunction = tmfuc;
            
            animations = [CAAnimationGroup animation];
            [animations setDuration:1.5f];
            [animations setAnimations:@[headAnimation, tailAnimation, endHeadAnimation, endTailAnimation]];
            animations.repeatCount = INFINITY;
            
            [self.spinLayer addAnimation:animations forKey:kMMRingStrokeAnimationKey];
        }
    }
    else if (self.spinLayer)
    {
        [self.spinLayer removeAnimationForKey:kMMRingStrokeAnimationKey];
        [self.spinLayer removeAnimationForKey:kMMRingRotationAnimationKey];
        [self.spinLayer removeFromSuperlayer];
        self.spinLayer = nil;//这里设为nil是因为动态绑定的为OBJC_ASSOCIATION_ASSIGN，而这种类型在对象被销毁后，它的指针不会自动置为nil，再次访问会造成闪退
    }
    
}

#define kBlinkAnimationKey @"blinkAnimation"

-(void)blinkAnimate:(BOOL)animte
{
    CALayer *layer = self.layer;
    if(animte)
    {
        CAAnimationGroup *animationGroup = (CAAnimationGroup *)[layer animationForKey:kBlinkAnimationKey];
        if(!animationGroup)
        {
            float duration = 1.2;
            
            CAMediaTimingFunction *defaultCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
            
            animationGroup = [CAAnimationGroup animation];
            animationGroup.duration = duration;
            animationGroup.repeatCount = INFINITY;
            animationGroup.removedOnCompletion = NO;
            animationGroup.timingFunction = defaultCurve;
            
           // NSArray *keyTimes = @[@0.0,@0.2,@0.9,@0.95,@1.0];
            
            CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.xy"];
            scaleAnimation.duration = duration;
            scaleAnimation.values = @[@0.95, @1.0, @1.05, @1.0, @0.95];
        //    scaleAnimation.keyTimes = keyTimes;
            scaleAnimation.removedOnCompletion = NO;
            
            CAKeyframeAnimation *opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
            opacityAnimation.duration = duration;
            opacityAnimation.values = @[@0.6, @0.9, @1.0, @0.6];
         //   opacityAnimation.keyTimes = keyTimes;
            opacityAnimation.removedOnCompletion = NO;
            
            NSArray *animations = @[scaleAnimation,opacityAnimation];
            
            animationGroup.animations = animations;
            
            [layer addAnimation:animationGroup forKey:kBlinkAnimationKey];
        }
        
    }
    else
    {
        [layer removeAnimationForKey:kBlinkAnimationKey];
    }
}


-(UIActivityIndicatorView *)activityIndicator{
    UIActivityIndicatorView * spin = (UIActivityIndicatorView *)[self viewWithTag:9988873];
    return spin;
}

-(void)activityIndicatorCheck {

    UIActivityIndicatorView * spin = (UIActivityIndicatorView *)[self viewWithTag:9988873];
    if(!spin) {
        
        spin = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:(UIActivityIndicatorViewStyleGray)];
        spin.hidesWhenStopped = YES;
        spin.tag = 9988873;
        [self addSubview:spin];
        spin.center =CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2);
        spin.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
    }
    
}

-(void)startActivityIndicator
{
    [self activityIndicatorCheck];
    [self.activityIndicator startAnimating];
}

-(void)stopActivityIndicator
{
    [self.activityIndicator stopAnimating];
    [self.activityIndicator removeFromSuperview];
}

@end



static char rOnselectionKey, bOnSelectionKey,sTintColorKey, tmpTintColorStoreKey;

@implementation UIButton (SpinAnimation)

+(void)load
{
    Class class = [self class];
    
    SEL originalSelector = @selector(setSelected:);
    SEL swizzledSelector = @selector(mob_setSelected:);
    method_exchangeImplementations(class_getInstanceMethod(class, originalSelector), class_getInstanceMethod(class, swizzledSelector));
    
}

-(void)setRingWhenSelected:(BOOL)ringWhenSelected
{
    objc_setAssociatedObject(self, &rOnselectionKey, [NSNumber numberWithBool:ringWhenSelected], OBJC_ASSOCIATION_RETAIN);
}

-(BOOL)ringWhenSelected
{
    NSNumber *val = objc_getAssociatedObject(self, &rOnselectionKey);
    return val.boolValue;
}

-(void)setBlinkWhenSelected:(BOOL)blinkWhenSelected
{
    objc_setAssociatedObject(self, &bOnSelectionKey, [NSNumber numberWithBool:blinkWhenSelected], OBJC_ASSOCIATION_RETAIN);
}

-(BOOL)blinkWhenSelected
{
    NSNumber *val = objc_getAssociatedObject(self, &bOnSelectionKey);
    return val.boolValue;
}

-(void)setSelectedTintColor:(UIColor *)selectedTintColor
{
    objc_setAssociatedObject(self, &sTintColorKey, selectedTintColor, OBJC_ASSOCIATION_RETAIN);
    
    if(!selectedTintColor)
    {
        UIColor *color = objc_getAssociatedObject(self, &tmpTintColorStoreKey);
        if(color)
            self.tintColor = color;
    }
}

-(UIColor *)selectedTintColor
{
    UIColor *color = objc_getAssociatedObject(self, &sTintColorKey);
    return color;
}



-(void)mob_setSelected:(BOOL)selected
{
    BOOL _preSelected = self.selected;
    
    [self mob_setSelected:selected];
    
    if(self.selectedTintColor)
    {
        if(selected)
        {
            //first save the pre tintColor, then set the new tintColor
            if(!_preSelected)
                objc_setAssociatedObject(self, &tmpTintColorStoreKey, self.tintColor, OBJC_ASSOCIATION_RETAIN);
            self.tintColor = self.selectedTintColor;
        }
        else
        {
            //restore tintColor
            UIColor *color = objc_getAssociatedObject(self, &tmpTintColorStoreKey);
            if(color)
                self.tintColor = color;
        }
    }
    
    if(self.ringWhenSelected)
        [self ringAnimate:selected];
    
    if(self.blinkWhenSelected)
        [self blinkAnimate:selected];
    

}

@end

@interface ArcProgressView ()

@property (nonatomic, strong) CAShapeLayer *prgLayer;
@property (nonatomic, strong) CAShapeLayer *trackLayer;
@end

@implementation ArcProgressView
{
    __weak UIView *_observingView;
    UILabel *_prgLabel;
}

-(id)initWithFrame:(CGRect)arcFrame
{
    self = [super initWithFrame:arcFrame];
    if(self)
    {
        [self centerToFrame:arcFrame];
        self.progressTintColor = self.tintColor;
        self.lineWidth = 2.0;
        self.trackTintColor = [UIColor colorWithWhite:0.87 alpha:0.92];
        self.userInteractionEnabled = NO;
        
        self.backgroundColor = [UIColor clearColor];
        
        
        CAShapeLayer *theLayer = [CAShapeLayer layer];
    
        theLayer.frame = self.bounds;
        theLayer.strokeColor = self.progressTintColor.CGColor;
        theLayer.fillColor = nil;
        theLayer.lineWidth = self.lineWidth;
        theLayer.strokeStart = 0.f;
        theLayer.strokeEnd = 0.0;
        theLayer.lineCap = @"round";


        CAShapeLayer *layer2 = [CAShapeLayer layer];
        layer2.frame = self.bounds;
        layer2.strokeColor = self.trackTintColor.CGColor;
        layer2.fillColor = nil;
        layer2.lineWidth = self.lineWidth;

        
        [self.layer addSublayer:layer2];
        [self.layer addSublayer:theLayer];
        
        self.prgLayer = theLayer;
        self.trackLayer = layer2;

        [self updateArc];
    }
    return self;
}

-(void)centerToFrame:(CGRect)frame
{
    self.frame = frame;
    self.center = CGPointMake(frame.origin.x+frame.size.width/2, frame.origin.y+frame.size.height/2);
}

-(void)setTrackTintColor:(UIColor *)trackTintColor
{
    _trackTintColor = trackTintColor;
    self.trackLayer.strokeColor = trackTintColor.CGColor;
}

-(void)setProgressTintColor:(UIColor *)progressTintColor
{
    _progressTintColor = progressTintColor;
    self.prgLayer.strokeColor = _progressTintColor.CGColor;
}


-(void)setProgressValue:(float)progressValue
{
    self.prgLayer.strokeEnd = progressValue;
    
}

-(float)progressValue
{
    return self.prgLayer.strokeEnd;
}

-(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
{
    [super setUserInteractionEnabled:NO];
}

-(void)setLineWidth:(CGFloat)lineWidth
{
    CGFloat preWidth = _lineWidth;
     _lineWidth = lineWidth;
    if(self.prgLayer || self.trackLayer)
    {
        if(preWidth != lineWidth)
        {
            self.prgLayer.lineWidth = lineWidth;
            self.trackLayer.lineWidth = lineWidth;
            [self updateArc];
        }
    }
   
}

+(instancetype)prgArcViewAroundView:(UIView *)view
{
    ArcProgressView *theLayer = [[self alloc]initWithFrame:view.frame];
    [view addObserver:theLayer forKeyPath:@"frame" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    theLayer->_observingView = view;
    
    return theLayer;
}

-(void)updateArc
{
    self.prgLayer.frame = self.bounds;
    self.trackLayer.frame = self.bounds;
   
  //  CGFloat lineW = self.lineWidth;
    
    CGPoint center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
    CGFloat radius = (MAX(self.frame.size.width, self.frame.size.height) + self.lineWidth)/2;
    CGFloat startAngle = -(CGFloat)(M_PI_2);
    CGFloat endAngle = (CGFloat)(1.5*M_PI);
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
    self.trackLayer.path = path.CGPath;
    self.prgLayer.path = [(UIBezierPath *)[path copy] CGPath];
}

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context
{
    if([keyPath isEqualToString:@"frame"])
    {
        NSValue *oldV = [change objectForKey:@"old"];
        NSValue *newV = [change objectForKey:@"new"];
        
        CGRect oldFrame = [oldV CGRectValue];
        CGRect newFrame = [newV CGRectValue];
        
        //[self centerToFrame:newFrame];
        self.frame = newFrame;

        if(oldFrame.size.width != newFrame.size.width || oldFrame.size.height != newFrame.size.height)
        {
            [self updateArc];
        }
        
    }
}

-(void)dealloc
{
    [_observingView removeObserver:self forKeyPath:@"frame"];
}

@end



